package org.bing.zion.filter.serializer;

import java.io.IOException;
import java.util.Date;

import org.bing.engine.common.logging.Log;
import org.bing.engine.common.logging.LogFactory;


public class SimpleTypeCodec {
	protected static final Log logger = LogFactory.getLog(SimpleTypeCodec.class);
	private static final SimpleTypeHelper typeHelper = new SimpleTypeHelper();

	protected boolean isSimpleType(Object obj) {
		return typeHelper.isSimpleType(obj);
	}

	protected boolean isSimpleType(String str) {
		return typeHelper.isSimpleType(str);
	}

	@SuppressWarnings("unchecked")
	protected String fineSimpleTypeName(Class clz) {
		return typeHelper.fineClzName(clz);
	}

	protected void simpleFieldEncode(String fieldName, Object fieldValue, SimpleXmlWriter out) throws IOException {
		out.beginElement("f");
		out.addAttribute("n", fieldName);
		out.addAttribute("c", typeHelper.fineClzName(fieldValue.getClass()));
		// fix xml problem
		if (fieldValue instanceof String) {
			String s = (String) fieldValue;
			out.addAttribute("s", s.length());
		}
		out.endAttribute();
		out.addText(simpleObjectToString(fieldValue));
		out.endElement("f");
	}

	protected String simpleObjectToString(Object obj) {
		if (obj instanceof Date) {
			return Long.toString(((Date) obj).getTime());
		} else if (obj instanceof String) {
			return escape((String) obj);
		} else {
			return obj.toString();
		}
	}

	protected void simpleObjectEncode(Object object, SimpleXmlWriter out) throws IOException {
		out.beginElement("o");
		out.addAttribute("c", typeHelper.fineClzName(object.getClass()));
		// fix xml problem
		if (object instanceof String) {
			String s = (String) object;
			out.addAttribute("s", s.length());
		}
		out.endAttribute();
		out.addText(simpleObjectToString(object));
		out.endElement("o");
	}

	protected Object simpleDecode(String s) {
		String type = this.readClazzName(s);
		String value = this.readSimpleValue(s);
		if (type != null) {
			return decodeSimpleType(type, value);
		}
		throw new SimpleSerializeException("Can't decode simple type from " + s);
	}

	protected String readSimpleValue(String s) {
		int b = s.indexOf('>');
		int e = s.indexOf('<', b + 1);
		if (b > 0 && e > 0) {
			return s.substring(b + 1, e);
		} else {
			return null;
		}
	}

	protected String readClazzName(String s) {
		int b = s.indexOf(" c=\"");
		int e = s.indexOf("\"", b + 5);
		if (b > 0 && e > 0) {
			return s.substring(b + 4, e);
		} else {
			return null;
		}
	}

	protected String readFieldName(String s) {
		int b = s.indexOf(" n=\"") + 4;
		int e = s.indexOf("\"", b + 1);
		if (b > 0 && e > 0) {
			return s.substring(b, e);
		} else {
			return null;
		}
	}

	protected int readObjectId(String s) {
		int b = s.indexOf(" id=\"") + 5;
		int e = s.indexOf("\"", b + 1);
		if (b > 0 && e > 0) {
			return Integer.parseInt(s.substring(b, e));
		} else {
			return -1;
		}
	}

	protected int readObjectRef(String s) {
		int b = s.indexOf(" ref=\"") + 6;
		int e = s.indexOf("\"", b + 1);
		if (b > 0 && e > 0) {
			return Integer.parseInt(s.substring(b, e));
		} else {
			return -1;
		}
	}

	protected int readLength(String s) {
		int b = s.indexOf(" s=\"");
		int e = s.indexOf("\"", b + 5);
		if (b > 0 && e > 0) {
			String len = s.substring(b + 4, e);
			return Integer.parseInt(len);
		} else {
			return -1;
		}
	}

	protected Object newInstance(String clazz) {
		try {
			return Class.forName(clazz).newInstance();
		} catch (Exception e) {
			throw new SimpleSerializeException("Fail to new object for " + clazz, e);
		}
	}

	// '<' => 10 and '>' => 11
	private static final char lt = (char) 19, rt = (char) 20;

	private String escape(String s) {
		char[] chars = s.toCharArray();
		int len = chars.length;
		for (int i = 0; i < len; i++) {
			char c = chars[i];
			if (c == '<') {
				chars[i] = lt;
			} else if (c == '>') {
				chars[i] = rt;
			}
		}
		return new String(chars);
	}

	private String unescape(String s) {
		char[] chars = s.toCharArray();
		int len = chars.length;
		for (int i = 0; i < len; i++) {
			char c = chars[i];
			if (c == lt) {
				chars[i] = '<';
			} else if (c == rt) {
				chars[i] = '>';
			}
		}
		return new String(chars);
	}

	private Object decodeSimpleType(String type, String s) {
		if ("string".equals(type)) {
			return unescape(s);
		} else if ("date".equals(type)) {
			return new Date(Long.parseLong(s));
		} else if ("null".equals(type)) {
			return null;
		} else if ("int".equals(type)) {
			return Integer.valueOf(s);
		} else if ("long".equals(type)) {
			return Long.valueOf(s);
		} else if ("boolean".equals(type)) {
			return Boolean.parseBoolean(s);
		} else if ("char".equals(type)) {
			return s.charAt(0);
		} else if ("byte".equals(type)) {
			return Byte.valueOf(s);
		} else if ("short".equals(type)) {
			return Short.valueOf(s);
		} else if ("float".equals(type)) {
			return Float.valueOf(s);
		} else if ("double".equals(type)) {
			return Double.valueOf(s);
		} else {
			throw new SimpleSerializeException("Can't decode " + type);
		}

	}
}
